//*****************************************************************************
// eventQueuePrivate.h
//
// Copyright 2009 by Magpie, http://www.magpieti.com
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//*****************************************************************************

#ifndef EVENT_QUEUE_PRIVATE_H
#define EVENT_QUEUE_PRIVATE_H

#ifdef RUNNING_UNIT_TESTS

  #include "eventQueueTest.h"

#else

  // Hardware specific includes would go here...

#endif // !RUNNING_UNIT_TESTS

// Structure used to contain event
typedef struct
{
  int32u eventId;
  int32u startTime;
  int32u effectiveStartTime;
  int32u endTime;
  int32u effectiveEndTime;
  int16u duration;
  boolean adjacentToNextEvent;
  boolean superseded;
  boolean used;
  boolean active;
} eventElement_t;

// Here are all of the non-public functions contained in the module.
// They are included here rather than in the source file so that unit
// tests can perform white box testing

//*****************************************************************************
// initializeEvent
//
// Sets event to a known state and marks as unused
//
//*****************************************************************************
void initializeEvent(eventElement_t* event);

//*****************************************************************************
// validStartAndEndTime
//
// Validate that the event can be constructed in a manner that
// results in a non-zero duration event with an effective start
// time that occurs before the requested end time. Should this
// condition not be true, return FALSE.
//*****************************************************************************
boolean validStartAndEndTime(int32u startTime, int16u durationInMinutes,
                             int32u currentTime);

//*****************************************************************************
// addNewEventToQueuePrivate
//
//  Queues the event for execution. Assumes that all static input validation
//  has already occurred.
//
//*****************************************************************************
void addNewEventToQueuePrivate(int32u startTime, int16u durationInMinutes,
                               int32u eventId, int32u currentTime);

//*****************************************************************************
// getQueuedEventCount
//
// Accessor, returns how many events are currently queued.
//
//*****************************************************************************
int8u getQueuedEventCount(void);

//*****************************************************************************
// getCurrentEvent
//
// Accessor, returns the event with the earliest start time.
//
// NOTE: returns NULL if queue is empty
//
//*****************************************************************************
eventElement_t* getCurrentEvent(void);

//*****************************************************************************
// getIndexedEvent
//
// Accessor, returns the sorted event with the specified index.
//
// NOTE: returns NULL if index is invalid or event is not used
//
//*****************************************************************************
eventElement_t* getIndexedEvent(int8u index);

//*****************************************************************************
// activateCurrentEvent
//
// If there is at least one event, the event with the earliest start time is
// marked as active.
//
//*****************************************************************************
void activateCurrentEvent(void);

//*****************************************************************************
// eventActive
//
//  Returns true if there is an event in progress.
//
//*****************************************************************************
boolean eventActive(void);

//*****************************************************************************
// eventQueued
//
// Returns true if there is at least one event queued
//
//*****************************************************************************
boolean eventQueued(void);

//*****************************************************************************
// sortEvents
//
// Sort events based upon start time (earliest first), algorithm is simple 
// bubble sort. Not the most efficient, but for the small number of events we
// handle, should not be a problem and it minimizes the code needed.
//*****************************************************************************
void sortEvents(void);

//*****************************************************************************
// swapNeeded
//
// Determines if two adjacent events should be swapped. Rules used:
//
// 1.) If the first event is not used and the second is, swap'm
// 2.) If both events are used and the start time of the first
//     is later than the start time of the second, swap'm
// 3.) Otherwise, no swap is needed.
//
//*****************************************************************************
boolean swapNeeded(eventElement_t* event1, eventElement_t* event2);

//*****************************************************************************
// handlePossibleSupersede
//
// This routine checks for queued events that may supersede other queued 
// events and adjusts the events when this occurs.
//
// Assumption: sortedEvents has been called prior to calling this routine, so
//             events are sorted.
//
// Note1: It is possible for a single event to overlap, and therefore supersede, 
//        multiple events. To handle this, the routine will possibly iterate 
//        multiple times.
//
// Note2: If an event superseded multiple events that are subsequently removed,
//        it might be necessary to query for new events to fill the queue.
//
// Note3: This test inherently works with a target event that has the same start
//        time as another event, regardless of the order in which they were or
//        not sorted.
//
// Here are the rules:
//
// The event, targetEvent, passed into the routine, is the most recently received
// event and the one that can supersede other events.
//
// Start with the second event in the list, so if only one event, we're done.
//
// Based upon where we are in the list, calculate the currentEvent and
// previousEvent.
//
// If the previousEvent is the targetEvent, then the targetEvent start time 
// occurs on or before the currentEvent start time, since the events have been 
// sorted prior to calling this routine. The only way for the targetEvent to 
// supersede the currentEvent is if the endTime of the targetEvent overlaps 
// the startTime of the current event. If this is true, supersede the 
// currentEvent.
//
// If the currentEvent is the targetEvent, then, by the same logic as above, the 
// previousEvent start time occurs on or before the targetEvent start time. The 
// only way for the targetEvent to supersede the previousEvent is if the endTime 
// of the previousEvent overlaps the startTime of the targetEvent. If this is 
// true, supersede the previousEvent, with one caveat...
//
// There is one edge case in which the previous event "might" be the first 
// event in the list and "might" be active. In this case, the event is not 
// removed from the list (per spec). Instead, the effective end time is 
// adjusted and it is marked as superseded. Since the algorithm used in this 
// routine is iterative, if a previousEvent is marked as superseded, we need 
// to skip it as it has already been adjusted.
//*****************************************************************************
void handlePossibleSupersede(eventElement_t* targetEvent);

//*****************************************************************************
// supersedeEvent
//
// This routine supersedes the supplied event with the target event.
//
// If an event is not active, supersede is just reporting an event as 
// superseded and removing it from the queue
//
// If the event to supersede is active, the event is marked as superseded and 
// the effective end time of the event is set to the start time of the 
// superceding event. The fact that the event has been superseded will be
// reported when it ends.
//
//*****************************************************************************
void supersedeEvent(eventElement_t* eventToSupersede,
                    eventElement_t* targetEvent);

//*****************************************************************************
// deleteEvent
//
// This routine marks the supplied event as unused and sorts it to the bottom
// of the queue.
//
//*****************************************************************************
void deleteEvent(eventElement_t* event);

//*****************************************************************************
// eventFromEventId
//
// Returns the event corresponding to the supplied event ID. If it is not
// found, NULL is returned.
//
//*****************************************************************************
eventElement_t* eventFromEventId(int32u eventId);

//*****************************************************************************
// checkEventEnd
//
// Checks to see if the supplied event, which is active, should be ended. If so:
//
//   1.) Call the client's disable event, noting whether an new event
//       is scheduled to start immediately
//   2.) Supply the correct status regarding how the event ended
//   3.) Delete the event
//   4.) Return TRUE, indicating an event was ended
//
//   Otherwise, return FALSE indicating no end occurred.
//*****************************************************************************
boolean checkEventEnd(eventElement_t* event, int32u currentTime);

//*****************************************************************************
// checkEventStart
//
// Checks to see if the supplied event, which is not active, should be started.
// If so:
//
//   1.) Mark the event as active
//   2.) Call the client's enable event function
//   2.) Send the EVENT_STARTED status
//
//*****************************************************************************
void checkEventStart(eventElement_t* event, int32u currentTime);

//*****************************************************************************
// sendStatus
//
// Send the supplied status for the supplied event.
//
//*****************************************************************************
void sendStatus(eventElement_t* event, int8u eventStatus);

//*****************************************************************************
// dumpEvent
//
// Dump the contents of the specified event.
//
//*****************************************************************************
void dumpEvent(eventElement_t *event);

//*****************************************************************************
// dumpAllEvents
// 
// Dump the sorted event queue, including unused events.
//
//*****************************************************************************
void dumpAllEvents(void);

#include "loggingSupport.h"
#include "eventQueue.h"

#endif //EVENT_QUEUE_PRIVATE_H
